package com.ease.util.proxies {

   import com.ease.util.HessianRemoteService;
   import com.ease.util.IResultHandler;
   import com.ease.util.ObjectEvent;
   import com.ease.util.RemoteService;
   import com.ease.util.StringUtils;
   import com.ease.util.events.MethodReturnEvent;
   import com.ease.util.logging.LogManager;

   import flash.events.Event;

   import mx.core.FlexGlobals;
   import mx.logging.ILogger;
   import mx.rpc.AbstractOperation;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;

   public class BaseProxy {
      private static var _logger:ILogger = LogManager.getLogger(BaseProxy);
      private var _service:*;
      private var _resultHandler:IResultHandler;
      private var _opInvoker:OperationInvoker;

      public function BaseProxy(serviceName:String, channelUri:String = null, secure:Object = null, useAMF:Boolean = true) {
         FlexGlobals.topLevelApplication.dispatchEvent(ObjectEvent.newAddition(this));
         FlexGlobals.topLevelApplication.dispatchEvent(ObjectEvent.newRemoval(this));

         _service = useAMF? RemoteService.getInstance(serviceName, secure) : HessianRemoteService.getInstance(serviceName);
//         _service = RemoteService.getInstance(serviceName, secure);
         if (_service == null) {
            _logger.error("Failed to instantiate service: " + serviceName);
            return;
         }

         if (! StringUtils.isNullorEmpty(channelUri)) {
            _service.serviceChannelUri = channelUri;
         }
         _opInvoker = new OperationInvoker();
      }

      [Injectable]
      /**
       * @private
       */
      public function get resultHandler():IResultHandler {
         return _resultHandler;
      }

      public function set resultHandler(value:IResultHandler):void {
         _resultHandler = value;
      }

      /**
       * Sets the channel Uri
       *
       * @param channelUri The channel Uri associated with the underlying service.
       */
      public function set channelUri(channelUri:String):void {
         if (StringUtils.isNullorEmpty(channelUri)) {
            _logger.error("Setting the channel Uri to an empty or null string.");
            return;
         }
         _service.serviceChannelUri = channelUri;
      }

      /**
       * Calls a remote service Java method.
       *
       * @param methodName Java method name to be invoked.
       * @param arguments method arguments.
       * @param callBack <code>function(ev:MethodReturnEvent)</code>
       * @param callContext object that needs to be passed back to caller via callback.
       */
      public function callService(methodName:String, arguments:Array,
                                  callback:Function=null, callContext:Object=null):void {
         var operation:AbstractOperation = _service[methodName];
         var opContext:Object = {orgCallback : callback,
            orgCallContext : callContext};
         _opInvoker.invoke(operation, arguments, onInvocationComplete, opContext);
      }

      /**
       * Invokes the supplied callback with callContext and unpacked result.
       */
      protected function result(
         event:ResultEvent,
         callback:Function,
         callContext:Object):void {
         if (callback == null || event == null) {
            return;
         }
         // Create ResultEvent and call the callback function
         var returnEvent:MethodReturnEvent =
            MethodReturnEvent.newResultEvent(event.result, callContext);
         if (_resultHandler != null) {
            _resultHandler.handleResult(returnEvent, this);
         }
         notify(callback, returnEvent);
      }

      /**
       * Invokes the supplied callback with callContext and unpacked fault.
       */
      protected function fault(
         event:FaultEvent,
         callback:Function,
         callContext:Object):void {
         if (callback == null || event == null) {
            return;
         }
         // Create ErrorEvent and call the callback function
         var returnEvent:MethodReturnEvent =
            MethodReturnEvent.newErrorEvent(event.fault, callContext);
         if (_resultHandler != null) {
            _resultHandler.handleResult(returnEvent, this);
         }
         notify(callback, returnEvent);
      }

      /**
       * Notifies the caller with the given result event.
       */
      protected function notify(callback:Function, event:MethodReturnEvent):void {
         callback(event);
      }

      /**
       * Handles response of the invocation.
       */
      private function onInvocationComplete(event:Event, opContext:Object):void {
         var origCallback:Function = opContext.orgCallback;
         var origCallContext:Object = opContext.orgCallContext;
         if (event is ResultEvent) {
            result(event as ResultEvent,  origCallback, origCallContext);
         } else if (event is FaultEvent) {
            fault(event as FaultEvent,  origCallback, origCallContext);
         }
      }
   }
}